/**
 * Copyright 2020 jianggujin (www.jianggujin.com).
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *  
 *      http://www.apache.org/licenses/LICENSE-2.0
 *  
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.jianggujin.dbfly.mybatis.util;

import java.lang.reflect.Method;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import com.jianggujin.dbfly.mybatis.JConfiguration;
import com.jianggujin.dbfly.mybatis.builder.JParamConsts;
import com.jianggujin.dbfly.mybatis.constant.JIdGeneratorStrategy;
import com.jianggujin.dbfly.mybatis.entity.JEntity;
import com.jianggujin.dbfly.mybatis.entity.JEntityColumn;
import com.jianggujin.dbfly.mybatis.entity.JEntityIdColumn;
import com.jianggujin.dbfly.mybatis.id.JIdGeneratorUtils;
import com.jianggujin.dbfly.mybatis.table.JTableNameGeneratorUtils;
import com.jianggujin.dbfly.mybatis.value.JValueGeneratorUtils;
import com.jianggujin.dbfly.mybatis.version.JVersionGeneratorUtils;
import com.jianggujin.dbfly.util.JStringUtils;

public class JProviderHelper {

    /**
     * 解析实体对象
     * 
     * @param configuration
     * @param mapperClass
     * @param method
     * @return
     */
    public static JEntity resolveEntity(JConfiguration configuration, Class<?> mapperClass, Method method) {
        Class<?> entityClass = JMybatisUtils.getEntityClass(mapperClass, method.getDeclaringClass());
        return resolveEntity(configuration, entityClass);
    }

    /**
     * 解析实体对象
     * 
     * @param configuration
     * @param mapperClass
     * @param declaringClass
     * @return
     */
    public static JEntity resolveEntity(JConfiguration configuration, Class<?> mapperClass, Class<?> declaringClass) {
        Class<?> entityClass = JMybatisUtils.getEntityClass(mapperClass, declaringClass);
        return resolveEntity(configuration, entityClass);
    }

    /**
     * 解析实体对象
     * 
     * @param configuration
     * @param entityClass
     * @return
     */
    public static JEntity resolveEntity(JConfiguration configuration, Class<?> entityClass) {
        return configuration.getDBFlyConfiguration().getEntityResolver().resolveEntity(entityClass);
    }

    /**
     * 获得结果映射id
     * 
     * @param entityClass
     * @return
     */
    public static String getResultMap(Class<?> entityClass) {
        return JStringUtils.format("Base{}ResultMap", entityClass.getSimpleName());
    }

    /**
     * 判断是否存在指定结果映射
     * 
     * @param mapperElement
     * @param resultMap
     * @return
     */
    public static boolean hasResultMap(Element mapperElement, String resultMap) {
        NodeList nodeList = mapperElement.getElementsByTagName("resultMap");
        if (nodeList != null && nodeList.getLength() > 0) {
            int len = nodeList.getLength();
            for (int i = 0; i < len; i++) {
                Element element = (Element) nodeList.item(i);
                if (resultMap.equals(element.getAttribute("id"))) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 创建结果映射
     * 
     * @param configuration
     * @param document
     * @param mapperElement
     * @param resultMap
     * @param entity
     */
    public static void createResultMap(JConfiguration configuration, Document document, Element mapperElement,
            String resultMap, JEntity entity) {
        Element resultMapEle = JElementBuilder.buildResultMapElement(document, resultMap,
                entity.getEntityClass().getCanonicalName());
        List<JEntityColumn> columns = entity.getColumns();
        for (JEntityColumn column : columns) {
            if (!column.isSelectable()) {
                continue;
            }
            Element itemElement = JElementBuilder.buildElement(document, column.isId() ? "id" : "result", "property",
                    column.getProperty(), "column", column.getColumn());
            if (configuration.getDBFlyConfiguration().isUseJavaType()) {
                itemElement.setAttribute("javaType", column.getJavaType().getCanonicalName());
            }
            if (column.getJdbcType() != null) {
                itemElement.setAttribute("jdbcType", column.getJdbcType().name());
            }
            if (column.getTypeHandler() != null) {
                itemElement.setAttribute("typeHandler", column.getTypeHandler().getCanonicalName());
            }
            resultMapEle.appendChild(itemElement);
        }
        mapperElement.appendChild(resultMapEle);
    }

    /**
     * 检查并创建结果映射
     * 
     * @param configuration
     * @param document
     * @param mapperElement
     * @param entity
     * @return
     */
    public static String checkAndCreateResultMap(JConfiguration configuration, Document document, Element mapperElement,
            JEntity entity) {
        String resultMap = JProviderHelper.getResultMap(entity.getEntityClass());
        // 判断是否已经添加
        if (!JProviderHelper.hasResultMap(mapperElement, resultMap)) {
            JProviderHelper.createResultMap(configuration, document, mapperElement, resultMap, entity);
        }
        return resultMap;
    }

    /**
     * 选择所有可用列，形如：select xxx, xxx
     * 
     * @param document
     * @param parentElement
     * @param entity
     */
    public static void selectAllColumns(Document document, Element parentElement, JEntity entity) {
        JElementBuilder.appendChild(document, parentElement,
                JStringUtils.format("SELECT {} ", getAllColumns(entity, JEntityColumn::isSelectable)));
    }

    /**
     * 获取指定条件的所有列
     * 
     * @param entity
     * @param predicate
     * @return
     */
    public static String getAllColumns(JEntity entity, Predicate<? super JEntityColumn> predicate) {
        return entity.getColumns().stream().filter(predicate).map(JEntityColumn::getColumn)
                .collect(Collectors.joining(", "));
    }

    /**
     * 获取指定条件的所有值
     * 
     * @param entity
     * @param parameterName
     * @param predicate
     * @return
     */
    public static String getAllValues(JEntity entity, String parameterName,
            Predicate<? super JEntityColumn> predicate) {
        StringBuilder builder = new StringBuilder();
        boolean first = true;
        for (JEntityColumn column : entity.getColumns()) {
            if (!predicate.test(column)) {
                continue;
            }
            if (!first) {
                builder.append(JProviderHelper.getValuePart(column, ", ", null, parameterName));
            } else {
                builder.append(JProviderHelper.getValuePart(column, null, null, parameterName));
            }
            first = false;
        }
        return builder.toString();
    }

    /**
     * 表，形如：from XXX
     * 
     * @param configuration
     * @param document
     * @param parentElement
     * @param entity
     * @param parameterName
     */
    public static void fromTable(JConfiguration configuration, Document document, Element parentElement, JEntity entity,
            String parameterName) {
        JElementBuilder.appendChild(document, parentElement, " FROM ");
        dynamicTable(configuration, document, parentElement, entity, parameterName);
    }

    /**
     * 表，形如： XXX
     * 
     * @param configuration
     * @param document
     * @param parentElement
     * @param entity
     * @param parameterName
     */
    public static void dynamicTable(JConfiguration configuration, Document document, Element parentElement,
            JEntity entity, String parameterName) {
        if (entity.useDynamicTableName()) {
            JTableNameGeneratorUtils.dynamicTableName(configuration, document, parentElement, entity, parameterName);
        } else {
            JElementBuilder.appendChild(document, parentElement, entity.getFullTableName());
        }
    }

    /**
     * where所有列的条件，会判断是否!=null
     * 
     * @param document
     * @param parentElement
     * @param entity
     * @param parameterName
     */
    public static void whereAllIfColumns(Document document, Element parentElement, JEntity entity,
            String parameterName) {
        Element whereElement = JElementBuilder.buildWhereElement(document);
        String prefix = getParameterPrefix(parameterName);

        // 获取全部列
        List<JEntityColumn> columns = entity.getColumns();
        for (JEntityColumn column : columns) {
            Element ifElement = JElementBuilder.buildIfElement(document);
            if (column.isNotEmpty()) {
                ifElement.setAttribute("test", JStringUtils.format("{}{} != null and {}{} != ''", prefix,
                        column.getProperty(), prefix, column.getProperty()));
            } else {
                ifElement.setAttribute("test", JStringUtils.format("{}{} != null", prefix, column.getProperty()));
            }
            JElementBuilder.appendChild(document, ifElement,
                    JStringUtils.format(" AND {}", getColumnValueEqualsPart(column, null, null, parameterName)));
            whereElement.appendChild(ifElement);
        }
        parentElement.appendChild(whereElement);
    }

    /**
     * 获取默认的orderBy，通过注解设置的
     * 
     * @param document
     * @param parentElement
     * @param entity
     */
    public static void orderByDefault(Document document, Element parentElement, JEntity entity) {
        String orderByClause = entity.getOrderByClause();
        if (JStringUtils.isNotEmpty(orderByClause)) {
            JElementBuilder.appendChild(document, parentElement, JStringUtils.format(" ORDER BY {}", orderByClause));
        }
    }

    /**
     * where主键条件
     * 
     * @param document
     * @param parentElement
     * @param entity
     * @param parameterName
     */
    public static void wherePKColumns(Document document, Element parentElement, JEntity entity, String parameterName) {
        // 获取全部列
        List<? extends JEntityColumn> columns = null;
        if (entity.hasId()) {
            columns = entity.getIdColumns();
        } else {
            // 不存在主键，所有字段参与查询
            columns = entity.getColumns();
        }
        if (columns.isEmpty()) {
            return;
        }
        StringBuilder builder = new StringBuilder(" WHERE ");
        boolean first = true;
        for (JEntityColumn column : columns) {
            if (!first) {
                builder.append(getColumnValueEqualsPart(column, " AND ", null, parameterName));
            } else {
                builder.append(getColumnValueEqualsPart(column, null, null, parameterName));
            }
        }
        JElementBuilder.appendChild(document, parentElement, builder);
    }

    /**
     * [prefix]column = #{[parameterName.]property[, jdbcType=xxx][,
     * typeHandler=xxx][, javaType=xxx]}[suffix]
     * 
     * @param column
     * @param prefix
     * @param suffix
     * @param parameterName
     * @return
     */
    public static String getColumnValueEqualsPart(JEntityColumn column, String prefix, String suffix,
            String parameterName) {
        StringBuilder builder = new StringBuilder();
        if (JStringUtils.isNotEmpty(prefix)) {
            builder.append(prefix);
        }
        builder.append(column.getColumn()).append(" = ").append(getValuePart(column, null, suffix, parameterName));
        return builder.toString();
    }

    /**
     * [prefix]#{[parameterName.]property[, jdbcType=xxx][, typeHandler=xxx][,
     * javaType=xxx]}[suffix]
     * 
     * @param column
     * @param prefix
     * @param suffix
     * @param parameterName
     * @return
     */
    public static String getValuePart(JEntityColumn column, String prefix, String suffix, String parameterName) {
        StringBuilder builder = new StringBuilder();
        if (JStringUtils.isNotEmpty(prefix)) {
            builder.append(prefix);
        }
        builder.append("#{");
        if (JStringUtils.isNotEmpty(parameterName)) {
            builder.append(parameterName);
            builder.append(".");
        }
        builder.append(column.getProperty());
        // 如果 null 被当作值来传递，对于所有可能为空的列，JDBC Type 是需要的
        if (column.getJdbcType() != null) {
            builder.append(", jdbcType=");
            builder.append(column.getJdbcType().toString());
        }
        // 为了以后定制类型处理方式，你也可以指定一个特殊的类型处理器类，例如枚举
        if (column.getTypeHandler() != null) {
            builder.append(", typeHandler=");
            builder.append(column.getTypeHandler().getCanonicalName());
        }
        // 3.4.0 以前的 mybatis 无法获取父类中泛型的 javaType，所以如果使用低版本，就需要设置 useJavaType = true
        if (column.isUseJavaType() && !column.getJavaType().isArray()) {
            builder.append(", javaType=");
            builder.append(column.getJavaType().getCanonicalName());
        }
        builder.append("}");
        if (JStringUtils.isNotEmpty(suffix)) {
            builder.append(suffix);
        }
        return builder.toString();
    }

    /**
     * select count(x)
     * 
     * @param document
     * @param parentElement
     * @param entity
     */
    public static void selectCount(Document document, Element parentElement, JEntity entity) {
        JElementBuilder.appendChild(document, parentElement,
                JStringUtils.format("SELECT COUNT({}) ", getCountColumn(entity)));
    }

    /**
     * select case when count(x) &gt; 0 then 1 else 0 end
     * 
     * @param document
     * @param parentElement
     * @param entity
     */
    public static void selectCountExists(Document document, Element parentElement, JEntity entity) {
        JElementBuilder.appendChild(document, parentElement, JStringUtils
                .format("SELECT CASE WHEN COUNT({}) > 0 THEN 1 ELSE 0 END AS result ", getCountColumn(entity)));
    }

    /**
     * 获得用于count的列，如果有主键，取主键第一个元素，忽略联合主键情况
     * 
     * @param entity
     * @return
     */
    public static String getCountColumn(JEntity entity) {
        return entity.hasId() ? entity.getIdColumns().get(0).getColumn() : "*";
    }

    /**
     * delete tableName - 动态表名
     * 
     * @param configuration
     * @param document
     * @param parentElement
     * @param entity
     * @param parameterName
     */
    public static void deleteFromTable(JConfiguration configuration, Document document, Element parentElement,
            JEntity entity, String parameterName) {
        JElementBuilder.appendChild(document, parentElement, "DELETE FROM ");
        dynamicTable(configuration, document, parentElement, entity, parameterName);
    }

    /**
     * insert into tableName - 动态表名
     * 
     * @param configuration
     * @param document
     * @param parentElement
     * @param entity
     * @param parameterName
     */
    public static void insertIntoTable(JConfiguration configuration, Document document, Element parentElement,
            JEntity entity, String parameterName) {
        JElementBuilder.appendChild(document, parentElement, "INSERT INTO ");
        dynamicTable(configuration, document, parentElement, entity, parameterName);
    }

    /**
     * 用于insert，处理主键策略
     * 
     * @param document
     * @param parentElement
     * @param entity
     * @param parameterName
     */
    public static void selectKey(Document document, Element parentElement, JEntity entity, String parameterName) {
        if (entity.hasId()) {
            for (JEntityIdColumn column : entity.getIdColumns()) {
                JIdGeneratorStrategy strategy = column.getIdGeneratorStrategy();
                switch (strategy) {
                case IDENTITY:
                    String idGeneratorSql = column.getIdGeneratorSql();
                    Element selectKeyElement = JElementBuilder.buildSelectKeyElement(document, column);
                    JElementBuilder.appendChild(document, selectKeyElement, idGeneratorSql);
                    parentElement.appendChild(selectKeyElement);
                    break;
                case JAVA:
                    Element bindElement = JElementBuilder.buildBindElement(document,
                            JStringUtils.format("generateIdBind_{}", column.getProperty()),
                            JStringUtils.format("@{}@generateId({}, @{}@class, '{}')",
                                    JIdGeneratorUtils.class.getCanonicalName(), getParameterName(parameterName),
                                    entity.getEntityClass().getCanonicalName(), column.getProperty()));
                    parentElement.appendChild(bindElement);
                    break;
                case JDBC:
                    JElementBuilder.setAttributes(parentElement, "useGeneratedKeys", "true", "keyProperty",
                            column.getProperty());
                    break;
                case NONE:
                    break;
                }
            }
        }
    }

    /**
     * 用于批量insert，处理主键策略，只会处理主键策略位Java代码生成的主键
     * 
     * @param document
     * @param parentElement
     * @param entity
     * @param parameterName
     */
    public static void selectKeys(Document document, Element parentElement, JEntity entity, String parameterName) {
        if (entity.hasId()) {
            List<JEntityIdColumn> columns = entity.getIdColumns().stream()
                    .filter(item -> JIdGeneratorStrategy.JAVA == item.getIdGeneratorStrategy())
                    .collect(Collectors.toList());
            int size = columns.size();
            parameterName = getParameterName(parameterName);
            if (size == 1) {
                Element bindElement = JElementBuilder.buildBindElement(document,
                        JStringUtils.format("generateIdBind_{}", columns.get(0).getProperty()),
                        JStringUtils.format("@{}@generateId({}, @{}@class, '{}')",
                                JIdGeneratorUtils.class.getCanonicalName(), parameterName,
                                entity.getEntityClass().getCanonicalName(), columns.get(0).getProperty()));
                parentElement.appendChild(bindElement);
            } else if (size > 1) {
                String ids = columns.stream().map(JEntityIdColumn::getProperty).collect(Collectors.joining(","));
                Element bindElement = JElementBuilder.buildBindElement(document, "generateIdBinds",
                        JStringUtils.format("@{}@generateIds({}, @{}@class, '{}')",
                                JIdGeneratorUtils.class.getCanonicalName(), parameterName,
                                entity.getEntityClass().getCanonicalName(), ids));
                parentElement.appendChild(bindElement);
            }
        }
    }

    /**
     * insert table()列
     * 
     * @param document
     * @param parentElement
     * @param entity
     * @param notNull       是否判断!=null
     * @param parameterName
     */
    public static void insertColumns(Document document, Element parentElement, JEntity entity, boolean notNull,
            String parameterName) {
        if (notNull) {
            Element trimElement = JElementBuilder.buildTrimElement(document, "(", ")");
            trimElement.setAttribute("suffixOverrides", ",");
            String prefix = getParameterPrefix(parameterName);
            for (JEntityColumn column : entity.getColumns()) {
                if (!column.isInsertable()) {
                    continue;
                }
                Element ifElement = JElementBuilder.buildIfElement(document);
                if (column.isNotEmpty()) {
                    ifElement.setAttribute("test", JStringUtils.format("{}{} != null and {}{} != ''", prefix,
                            column.getProperty(), prefix, column.getProperty()));
                } else {
                    ifElement.setAttribute("test", JStringUtils.format("{}{} != null", prefix, column.getProperty()));
                }
                JElementBuilder.appendChild(document, ifElement, JStringUtils.format("{}, ", column.getColumn()));
                trimElement.appendChild(ifElement);
            }
            parentElement.appendChild(trimElement);
        } else {
            JElementBuilder.appendChild(document, parentElement,
                    JStringUtils.format("({})", JProviderHelper.getAllColumns(entity, JEntityColumn::isInsertable)));
        }
    }

    /**
     * insert table()值
     * 
     * @param document
     * @param parentElement
     * @param entity
     * @param notNull       是否判断!=null
     * @param parameterName
     */
    public static void insertValues(Document document, Element parentElement, JEntity entity, boolean notNull,
            String parameterName) {
        if (notNull) {
            Element trimElement = JElementBuilder.buildTrimElement(document, " VALUES (", ")");
            trimElement.setAttribute("suffixOverrides", ",");
            String prefix = getParameterPrefix(parameterName);
            for (JEntityColumn column : entity.getColumns()) {
                if (!column.isInsertable()) {
                    continue;
                }
                Element ifElement = JElementBuilder.buildIfElement(document);
                if (column.isNotEmpty()) {
                    ifElement.setAttribute("test", JStringUtils.format("{}{} != null and {}{} != ''", prefix,
                            column.getProperty(), prefix, column.getProperty()));
                } else {
                    ifElement.setAttribute("test", JStringUtils.format("{}{} != null", prefix, column.getProperty()));
                }
                JElementBuilder.appendChild(document, ifElement, getValuePart(column, null, ", ", parameterName));
                trimElement.appendChild(ifElement);
            }
            parentElement.appendChild(trimElement);
        } else {
            JElementBuilder.appendChild(document, parentElement, JStringUtils.format(" VALUES ({})",
                    JProviderHelper.getAllValues(entity, parameterName, JEntityColumn::isInsertable)));
        }
    }

    /**
     * update tableName - 动态表名
     * 
     * @param configuration
     * @param document
     * @param parentElement
     * @param entity
     * @param parameterName
     */
    public static void updateTable(JConfiguration configuration, Document document, Element parentElement,
            JEntity entity, String parameterName) {
        JElementBuilder.appendChild(document, parentElement, "UPDATE ");
        dynamicTable(configuration, document, parentElement, entity, parameterName);
    }

    /**
     * update set列
     * 
     * @param document
     * @param parentElement
     * @param entity
     * @param notNull       是否判断String类型!=''
     * @param parameterName 实体参数名称
     */
    public static void updateSetColumns(Document document, Element parentElement, JEntity entity, boolean notNull,
            String parameterName) {
        Element setElement = JElementBuilder.buildSetElement(document);
        for (JEntityColumn column : entity.getColumns()) {
            // 不允许修改或者主键，忽略
            if (!column.isUpdatable() || column.isId()) {
                continue;
            }
            String prefix = getParameterPrefix(parameterName);
            if (column.isVersion()) {
                Element bindElement = JElementBuilder.buildBindElement(document,
                        JStringUtils.format("{}NextVersion", column.getProperty()),
                        JStringUtils.format("@{}@generateVersion(@{}@class, '{}', {}{})",
                                JVersionGeneratorUtils.class.getCanonicalName(),
                                entity.getEntityClass().getCanonicalName(), column.getProperty(), prefix,
                                column.getProperty()));
                setElement.appendChild(bindElement);
                JElementBuilder.appendChild(document, setElement,
                        JStringUtils.format("{} = #{{}NextVersion}", column.getColumn(), column.getProperty()));
            } else if (notNull) {
                Element ifElement = JElementBuilder.buildIfElement(document);
                if (column.isNotEmpty()) {
                    ifElement.setAttribute("test", JStringUtils.format("{}{} != null and {}{} != ''", prefix,
                            column.getProperty(), prefix, column.getProperty()));
                } else {
                    ifElement.setAttribute("test", JStringUtils.format("{}{} != null", prefix, column.getProperty()));
                }
                JElementBuilder.appendChild(document, ifElement,
                        getColumnValueEqualsPart(column, null, ", ", parameterName));
                setElement.appendChild(ifElement);
            } else {
                JElementBuilder.appendChild(document, setElement,
                        getColumnValueEqualsPart(column, null, ", ", parameterName));
            }
        }
        parentElement.appendChild(setElement);
    }

    /**
     * conditon支持查询指定列时
     * 
     * @param document
     * @param parentElement
     * @param entity
     * @param parameterName
     */
    public static void conditionCountColumn(Document document, Element parentElement, JEntity entity,
            String parameterName) {
        Element chooseElement = JElementBuilder.buildChooseElement(document);
        Element whenElement = JElementBuilder.buildWhenElement(document, JStringUtils.format("@{}@hasCountColumn({})",
                JOGNL.class.getCanonicalName(), getParameterName(parameterName)));
        String prefix = getParameterPrefix(parameterName);
        JElementBuilder.appendChild(document, whenElement, "COUNT(");
        Element ifElement = JElementBuilder.buildIfElement(document, JStringUtils.format("{}distinct", prefix));
        JElementBuilder.appendChild(document, whenElement, "DISTINCT ");
        whenElement.appendChild(ifElement);
        JElementBuilder.appendChild(document, whenElement, JStringUtils.format("${{}countColumn})", prefix));
        chooseElement.appendChild(whenElement);
        Element otherwiseElement = JElementBuilder.buildOtherwiseElement(document);
        JElementBuilder.appendChild(document, otherwiseElement,
                JStringUtils.format("COUNT({})", getCountColumn(entity)));
        chooseElement.appendChild(otherwiseElement);
        parentElement.appendChild(chooseElement);
    }

    /**
     * condition支持查询指定列时
     * 
     * @param document
     * @param parentElement
     * @param entity
     * @param parameterName
     */
    public static void conditionSelectColumns(Document document, Element parentElement, JEntity entity,
            String parameterName) {
        Element chooseElement = JElementBuilder.buildChooseElement(document);
        Element whenElement = JElementBuilder.buildWhenElement(document, JStringUtils.format("@{}@hasSelectColumns({})",
                JOGNL.class.getCanonicalName(), getParameterName(parameterName)));
        Element foreachElement = JElementBuilder.buildForeachElement(document,
                JStringUtils.format("{}selectColumns", getParameterPrefix(parameterName)), "selectColumn", ",");
        JElementBuilder.appendChild(document, foreachElement, "${selectColumn}");
        whenElement.appendChild(foreachElement);
        chooseElement.appendChild(whenElement);
        Element otherwiseElement = JElementBuilder.buildOtherwiseElement(document);
        JElementBuilder.appendChild(document, otherwiseElement, getAllColumns(entity, JEntityColumn::isSelectable));
        chooseElement.appendChild(otherwiseElement);
        parentElement.appendChild(chooseElement);
    }

    /**
     * Condition查询中的where结构
     * 
     * @param document
     * @param parentElement
     * @param parameterName
     */
    public static void conditionWhereClause(Document document, Element parentElement, String parameterName) {
        Element ifElment = JElementBuilder.buildIfElement(document,
                JStringUtils.format("{} != null", getParameterName(parameterName)));
        Element whereElment = JElementBuilder.buildWhereElement(document);
        Element trimElement = JElementBuilder.buildTrimElement(document, null, null);
        trimElement.setAttribute("prefixOverrides", "and |or ");
        String collection = null;
        collection = JStringUtils.format("{}oredCriteria", getParameterPrefix(parameterName));
        Element foreachElement = JElementBuilder.buildForeachElement(document, collection, "criteria");
        Element validIfElement = JElementBuilder.buildIfElement(document, "criteria.valid");

        JElementBuilder.appendChild(document, validIfElement,
                JStringUtils.format("${@{}@andOr(criteria)} ", JOGNL.class.getCanonicalName()));

        Element ifTrimElement = JElementBuilder.buildTrimElement(document, "(", ")");
        ifTrimElement.setAttribute("prefixOverrides", "and |or ");
        Element trimForeachElement = JElementBuilder.buildForeachElement(document, "criteria.criteria", "criterion");
        Element chooseElement = JElementBuilder.buildChooseElement(document);
        Element noValueWhenElement = JElementBuilder.buildWhenElement(document, "criterion.noValue");
        JElementBuilder.appendChild(document, noValueWhenElement,
                JStringUtils.format("${@{}@andOr(criterion)} ${criterion.condition}", JOGNL.class.getCanonicalName()));
        chooseElement.appendChild(noValueWhenElement);
        Element singleValueWhenElement = JElementBuilder.buildWhenElement(document, "criterion.singleValue");
        JElementBuilder.appendChild(document, singleValueWhenElement, JStringUtils.format(
                "${@{}@andOr(criterion)} ${criterion.condition} #{criterion.value}", JOGNL.class.getCanonicalName()));
        chooseElement.appendChild(singleValueWhenElement);
        Element betweenValueWhenElement = JElementBuilder.buildWhenElement(document, "criterion.betweenValue");
        JElementBuilder.appendChild(document, betweenValueWhenElement, JStringUtils.format(
                "${@{}@andOr(criterion)} ${criterion.condition} #{criterion.value} and #{criterion.secondValue}",
                JOGNL.class.getCanonicalName()));
        chooseElement.appendChild(betweenValueWhenElement);
        Element listValueWhenElement = JElementBuilder.buildWhenElement(document, "criterion.listValue");
        JElementBuilder.appendChild(document, listValueWhenElement,
                JStringUtils.format("${@{}@andOr(criterion)} ${criterion.condition}", JOGNL.class.getCanonicalName()));
        Element listValueWhenForeachElement = JElementBuilder.buildForeachElement(document, "(", ")", "criterion.value",
                "listItem", ",");
        JElementBuilder.appendChild(document, listValueWhenForeachElement, "#{listItem}");
        listValueWhenElement.appendChild(listValueWhenForeachElement);
        chooseElement.appendChild(listValueWhenElement);
        trimForeachElement.appendChild(chooseElement);
        ifTrimElement.appendChild(trimForeachElement);
        validIfElement.appendChild(ifTrimElement);
        foreachElement.appendChild(validIfElement);
        trimElement.appendChild(foreachElement);
        whereElment.appendChild(trimElement);
        ifElment.appendChild(whereElment);
        parentElement.appendChild(ifElment);
    }

    /**
     * condition查询中的orderBy条件，会判断默认orderBy
     * 
     * @param document
     * @param parentElement
     * @param entity
     * @param parameterName
     */
    public static void conditionOrderBy(Document document, Element parentElement, JEntity entity,
            String parameterName) {
        String prefix = getParameterPrefix(parameterName);
        Element ifElement = JElementBuilder.buildIfElement(document,
                JStringUtils.format("{}orderByClause != null", prefix));
        JElementBuilder.appendChild(document, ifElement, JStringUtils.format("ORDER BY ${{}orderByClause}", prefix));
        parentElement.appendChild(ifElement);

        String orderByClause = entity.getOrderByClause();
        if (JStringUtils.isNotEmpty(orderByClause)) {
            ifElement = JElementBuilder.buildIfElement(document,
                    JStringUtils.format("{}orderByClause == null", prefix));
            JElementBuilder.appendChild(document, ifElement, JStringUtils.format("ORDER BY {}", orderByClause));
            parentElement.appendChild(ifElement);
        }
    }

    /**
     * condition支持for update
     * 
     * @param document
     * @param parentElement
     * @param parameterName
     */
    public static void conditionForUpdate(Document document, Element parentElement, String parameterName) {
        Element ifElement = JElementBuilder.buildIfElement(document, JStringUtils.format("@{}@hasForUpdate({})",
                JOGNL.class.getCanonicalName(), getParameterName(parameterName)));
        JElementBuilder.appendChild(document, ifElement, " FOR UPDATE");
        parentElement.appendChild(ifElement);
    }

    /**
     * 绑定自动生成的Value
     * 
     * @param document
     * @param parentElement
     * @param entity
     * @param isInsert
     * @param parameterName
     */
    public static void bindGeneratorValue(Document document, Element parentElement, JEntity entity, boolean isInsert,
            String parameterName) {
        List<JEntityColumn> columns = entity.getColumns().stream().filter(JEntityColumn::isValueGenerator)
                .collect(Collectors.toList());
        for (JEntityColumn column : columns) {
            if ((isInsert && column.isInsertable()) || (!isInsert && column.isUpdatable())) {
                Element bindElement = JElementBuilder.buildBindElement(document,
                        JStringUtils.format("generateValueBind_{}", column.getProperty()),
                        JStringUtils.format("@{}@generateValue({}, @{}@class, '{}')",
                                JValueGeneratorUtils.class.getCanonicalName(), getParameterName(parameterName),
                                entity.getEntityClass().getCanonicalName(), column.getProperty()));
                parentElement.appendChild(bindElement);
            }
        }
    }

    /**
     * 绑定自动生成的Value
     * 
     * @param document
     * @param parentElement
     * @param entity
     * @param parameterName
     */
    public static void bindGeneratorValues(Document document, Element parentElement, JEntity entity,
            String parameterName) {
        List<JEntityColumn> columns = entity.getColumns().stream()
                .filter(item -> item.isValueGenerator() && item.isInsertable()).collect(Collectors.toList());
        int size = columns.size();
        if (size == 1) {
            Element bindElement = JElementBuilder.buildBindElement(document,
                    JStringUtils.format("generateValueBind_{}", columns.get(0).getProperty()),
                    JStringUtils.format("@{}@generateValue({}, @{}@class, '{}')",
                            JValueGeneratorUtils.class.getCanonicalName(), getParameterName(parameterName),
                            entity.getEntityClass().getCanonicalName(), columns.get(0).getProperty()));
            parentElement.appendChild(bindElement);
        } else if (size > 1) {
            String ids = columns.stream().map(JEntityColumn::getProperty).collect(Collectors.joining(","));
            Element bindElement = JElementBuilder.buildBindElement(document, "generateValueBinds",
                    JStringUtils.format("@{}@generateValues({}, @{}@class, '{}')",
                            JValueGeneratorUtils.class.getCanonicalName(), getParameterName(parameterName),
                            entity.getEntityClass().getCanonicalName(), ids));
            parentElement.appendChild(bindElement);
        }
    }

    /**
     * 获得参数前缀
     * 
     * @param parameterName
     * @return
     */
    public static String getParameterPrefix(String parameterName) {
        String prefix = "";
        if (JStringUtils.isNotEmpty(parameterName)) {
            prefix = JStringUtils.format("{}.", parameterName);
        }
        return prefix;
    }

    /**
     * 获得参数名称
     * 
     * @param parameterName
     * @return
     */
    public static String getParameterName(String parameterName) {
        return JStringUtils.isEmpty(parameterName) ? JParamConsts.MYBATIS_PARAMETER : parameterName;
    }
}
